项目介绍
需求分析
iOS原生风格的极简的日程管理工具。
现有的Todo工具:UI设计趋向于小清新、可爱动画等多元设计;功能为可以记录时间、地点、时间重要程度等信息,这样对于提高了增加任务的时间、精力成本。
本APP主打:界面使用iOS原生风格,随时想到、随时记录。
功能:用户可以对待办事项输入、修改、删除、修改顺序操作,可以标记已经完成任务。
使用场景:午餐时想到了一个idea直接打开APP记录、午睡后想到这个idea的实现可直接修改,晚上实现该功能后直接打钩,第二天测试人员告知该idea可行时删除任务。
功能需求
浏览任务页面:可以在tableview查看所有任务及其完成状态;可以在编辑状态下批量删除任务、移动任务顺序;按下navigation的+进入添加任务页面;按下>进入修改任务页面。
添加修改页面:判断当前任务是添加还是修改。使用自定义protocol和delegate反向传值,将文字传回浏览页面。
知识点
主线程
https://www.jianshu.com/p/f042432e2d7d
TableView的使用
1.我们知道tableView是IOS中的高级视图,其继承与ScrollView,具有ScrollView的功能,还扩展了许多。
2.tableView的表现格式分两种Plain和Grouped两种风格
3.tableView的两种代理类delegate和dataSource.这两种代理至关重要,我们在做tableView和这些代理是分不开的。
4.代理中比较常用的代理方法:
(1)dataSource的两个必须使用的代理
显示UITableView的Cell的个数:一共有多少个格子
Cell和model的数据的交互:根据数据,获取每一个数据的值
5.增删改查需要做的:第一步获取需要操作的行数(增加不需要),第二步对数据库进行处理,第三部更新视图
6.更新视图的2种方式:beginUpdates、endUpdates之前操作(如一行一行的删除数据);tableView.reloadData()刷新TableView了。
在使用数据库之后,我就直接reloadData()刷新TableView了。比较方便
1 | // 方式一:把批量对视图的操作防在两句话中间,可以提高app的性能 |
Navigation使用,sugue实现跳转
常用控件:相当于一个扑克盒子,里面的页面是出栈和入栈来实现试图切换。
利用segue界面跳转一共有两种方式:
第一种就是以上我的例子,利用button组件,拖拽添加segue,直接运行就可以用。
第二种是利用ViewController与ViewController之间,拖拽添加segue。不过,这种方法就需要在相应需要跳转的方法内写入代码,手动去设置它的跳转。
iOS 10中有几种sugue类型:
Show:新的view controller将被添加view controller栈的顶部。跳转的页面有Navigation bar,并且有返回原来页面的返回按钮。这是非常常用的的类型。
Show detail:在view controller栈中,新的view controller将被替代原来的view controller。跳转的页面没有Navigation bar,也没有返回原来页面的返回按钮。
Present modally:新页面将以动画形式从底部出现到覆盖整个手机屏幕。这种形式最常见的列子是iOS自带的日历应用:
项目布局
UI设计
页面一
页面二
步骤
配置tableview数据
重用单元格的形式,数据成千上万行,最终渲染个数为屏幕上显示的数目。
往下拉的时候,最上面的cell到最下面来,放置重复渲染,提高手机性能。
使用tableview cell
Cell定一个class为TodoCell,Cell使用as!强制转换为TodoCell。
model结构体使用Struct初探
结构体:不需要写初始化构造器,直接可以构造出来;轻量级的class
indexPath动态配置每行数据
确定位置:第几行indexPath.row、第几段indexPath.session
cell切换打勾和取消
使用static单元格,grouped风格。
navigation controller压栈(入栈)和出栈
navigation controller类似扑克盒子
跳转实现
主页面标题设置为大标题,需要在选中navigation页面,设置大标题
新页面需要设置小标题,添加item当做标题,再修改名称,改为大标题为never
返回按钮的名称需要在页面一的任务清单UINavigationItem中修改
添加任务功能✨
流程如下:
使用:(自定义protocol和delegate反向传值)+出栈
添加新任务:反向传值
确定之后页面消失:出栈,navigation老大让他出栈
编辑任务✨
1 正向传值segue将选中的cell中的(1 任务文本todo;2选中的行数row)传到编辑页面
2 编辑页面根据输入框判断是否有文本,定下title。若不为空,title为编辑任务;为空title为添加任务
3 修改后按下navigate bar button确认后,将(1修改后的文字;2之前选中的行数)传回主页面
4 主页面中实现协议的函数中:修改mode数组中的数据,根据行数row找到cell,再将cell的文字改为新的任务
左滑删除
已经预置代码,直接取消注释;
添加上删除某一行的数据;
更新页面已经写了,不需要添加。
批量选择+批量删除
navigation自带了添加编辑button的代码,取消注释,将button改到左边即可,还需要批量功能
由于需要批量的对象是table view,选中他,找到edit选择multiple 编辑即可
实现批量选中的效果:
批量删除需要重新放一个”删除“按钮;
在按下按钮后:获取选中的indexPath存储于数组,通过循环删除数据对应行数indexPath.row,使用table view的delete方法删除存于数组中的行数
1 | // 获取所有被选中的indexPath,若为空则不进行删除操作 |
ps 更新视图
// 方式一:把批量对视图的操作防在两句话中间,可以提高app的性能
tableView.beginUpdates()
// codes
tableView.endUpdates()
// 方式二:从新load数据更新页面;但没有动画效果 —— 就是相当于执行 cell for row的方法,将结果取出来,再更新视图
//tableView.reloadData()
发现无法选择,原因是:原先的设置选择后,立即变成未选中状态,使得页面保持白色,不会保持变成灰色。需要:在原先的选择代码中,使用flag——isEditing,判断若在editing状态,则可以选中、打钩等一系列操作。
1 | // 如果不是在编辑的情况下,选择打勾才有用、取消选中等才有用 |
按钮的默认文字汉化
左滑时候的delete —— 变成 ”删除“的关键词:tableView、title、delete、button后出现需要重写的方法
批量选择edit、done改成中文——1 通过command+点击获取方法、2通过三元运算符+isEditing确认是”编辑“还是”确定“
移动单元格
已有该方法,重写to support rearranging the table view
首先使用临时变量temp交换两个数据,接着更新视图moveRow(不写这一句也可以移动,即自动调用)。
但是这里存在一个小bug,当编辑状态下,当选中数据,被移动后,该数据将一直处于选中状态,且无法删除。结局方法为:移动数据后,reloadData,取消选中状态,就不会导致setEditing函数自动调用该函数,导致混乱。
1 | // 移动单元格功能 |
简单数据存储userDefaults
AppDelegate文件+轻量级本地存储userDefaults+编码解码
1 AppDelegate文件:APP的声明周期:
打开:APP启动、激活
按下home健:由于某种原因被挤到后台、进入后台
再次进入:即将激活、激活
退出:(APPlist 中)进入后台、(退出后)完全退出
2 数据
数据存储在沙盒里面,信息安全,本APP只能访问本APP的数据
沙盒数据中只能存储基本的数据类型,我们的数据是存储对象的Array,无法存储进沙盒plist,因此需要写一个存储函数来编码数据为.data
3 使用userDefaults
写好储存数据的方法(编码为data格式+存入沙盒)和解码方法(通过forKey获取数据,解码至todos数组):
1 |
|
再把写数据的函数在所有操作数据处理调用;
在程序load时候调用取数据。
本地存储coredata和realm
了解
cs和bs+什么是数据库+为什么App需要本地存储
cs:client-server —— QQ、微信——聊天记录存在本机
bs:browser-sever —— 网页论坛——数据存储在服务器
IOS本地存储数据库:
userdefaults——轻量级的数据
coredata——苹果自带的,学习成本高,代码多、不够快——了解
realm:速度快
realm安装与示例使用
由于cocoapods无法pod,我选择下载安装法,自行配置:
先去 Realm 的官网去下载最新框架: https://realm.io/cn/docs/swift/latest/#prerequisites
接着拖拽 RealmSwift.framework 和 Realm.framework 文件到”Embedded Binaries”选项中。选中 Copy items if needed 并点击 Finish
寻找应用的 Realm 文件:Swift using Realm Swift:(lldb) po Realm.Configuration.defaultConfiguration.fileURL
command+shift+g :跳转窗口,进入沙盒
在model中新建模型User.swift,import包,定义模型
1 | import Foundation |
进入AppDelegate,找到启动的方法,在该方法中:
1 数据实例化
do - catch中 2 创建数据库、3 存储数据
1 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { |
查看数据:print(Realm.Configuration.defaultConfiguration.fileURL)
command+shift+g :输入得到的地址,跳转窗口,进入沙盒,查看的结果
realm操作
1 在主页面处实例化数据库,为全局变量
1 | // 使用数据库后,todo相当于一个中间人的角色 |
2 创建RealmSwift的类 class Todo
1 | import Foundation |
2 写数据
新增数据功能:
新增数据后,创建一个类Todo的todo对象,传入saveData
3 取数据
现在viewLoad()的时候,读取数据。发现需要results类型,所以下一步更改todos的类型
1 | // 从Realm数据库中读取数据 |
声明todos由数组类型改为复合类型,接受取出来的 result array的数据,定义为可选性,可能为空
1 | //定义为esults类型todos -- 定义复合数据类型 -- 名字:复合类型<类型> ——1种方式 |
可选性(todos为空),则初始的时候判断。
为此,进行初始判断:当todos取出来为空的时候,数组的行数为0,且出现文字——请添加任务
1 | // 二、每段有几行:第i个session的行数 |
在存储数据的时候,把更新页面直接改为reload()方法。
修改数据
1 修改任务部分 :获取当前行数,修改todos![indexPath.row].name = name;更新视图
2 状态更改部分:按下,状态取反,修改todos![indexPath.row].checked =取反;更新视图
删数据
删除数据
更新视图
1 | // Override to support editing the table view. |
Realm-搜索和排序+searchBar+收起软键盘+主线程
点击search bar 搜索,发生什么事情。
想到需要一个delegate协议。先遵守协议UISearchBarDelegate,再委托。告诉Todos Controller,search bar是老板(委托人),一会他有些申请会委托给controller做。
1 | // 实现搜索功能 |
当searchBar内容改变的时候,查看其是否为空,若为空,则‘’‘
搜索之后,根据创建时间排序。先在class 中添加时间属性为当前时间,搜索的时候按照时间倒叙:
1 | // 实现searchBar委托给todosController干的事情 |
收起键盘:学习:https://www.jianshu.com/p/f042432e2d7d
1 | // 二、在搜索栏清空后,需要显示所有数据,且收起键盘 |
未完成:
//需求:打√之后,就放置在最后,取消打钩,放置在最前
源码
1 |